package org.budo.mybatis.session.delegate;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.budo.mybatis.dao.MybatisDao;
import org.budo.mybatis.pagehelper.BudoMybatisPageHelper;
import org.budo.support.dao.page.Page;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author limingwei2
 */
@SuppressWarnings("rawtypes")
public class SqlSessionDelegate extends AbstractSqlSessionDelegate {
    private static final Logger log = Slf4j.getLogger();

    private MybatisDao mybatisDao;

    public SqlSessionDelegate(SqlSession sqlSession, MybatisDao mybatisDao) {
        super(sqlSession);
        this.mybatisDao = mybatisDao;
    }

    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
        try {
            super.select(statement, parameter, rowBounds, resultHandler);
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                this.select(statement, parameter, rowBounds, resultHandler);
            } else {
                log.error("#39 select error, statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds + ", resultHandler=" + resultHandler, e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds + ", resultHandler=" + resultHandler, e);
                }
            }
        }
    }

    public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
        try {
            return super.selectCursor(statement, parameter, rowBounds);
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                return this.selectCursor(statement, parameter, rowBounds);
            } else {
                log.error("#59 selectCursor error, statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds, e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds, e);
                }
            }
        }
    }

    /**
     * @see #selectList(String, Object)
     */
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
            List<E> list = super.selectList(statement, parameter, rowBounds);

            Page page = BudoMybatisPageHelper.PAGE_THREAD_LOCAL.get();
            if (null != page) { // 自动将count条数设到page对象
                Number recordCount = BudoMybatisPageHelper.getRecordCount(list);
                page.setRecordCount(recordCount);
            }

            return list;
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                return this.selectList(statement, parameter, rowBounds);
            } else {
                log.error("#90 selectList error, statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds, e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("statement=" + statement + ", parameter=" + parameter + ", rowBounds=" + rowBounds, e);
                }
            }
        }
    }

    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        try {
            return super.selectMap(statement, parameter, mapKey, rowBounds);
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                return this.selectMap(statement, parameter, mapKey, rowBounds);
            } else {
                log.error("#110 selectMap error, statement=" + statement + ", parameter=" + parameter + ", mapKey=" + mapKey + ", rowBounds=" + rowBounds, e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("statement=" + statement + ", parameter=" + parameter + ", mapKey=" + mapKey + ", rowBounds=" + rowBounds, e);
                }
            }
        }
    }

    public <T> T selectOne(String statement, Object parameter) {
        try {
            return super.selectOne(statement, parameter);
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                return this.selectOne(statement, parameter);
            } else {
                log.error("#130 selectMap error, statement=" + statement + ", parameter=" + parameter + ", e=" + e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("#135 statement=" + statement + ", parameter=" + parameter, e);
                }
            }
        }
    }

    public int update(String statement, Object parameter) {
        try {
            return super.update(statement, parameter);
        } catch (Throwable e) {
            Class<?> type = SqlSessionDelegateUtil.findEntityTypeFromNoResultMapError(e);
            if (null != type) {
                mybatisDao.assertResultMap(type);
                return this.update(statement, parameter);
            } else {
                log.error("#150 update error, statement=" + statement + ", parameter=" + parameter, e);

                if (e instanceof RuntimeException) {
                    throw (RuntimeException) e;
                } else {
                    throw new RuntimeException("#155 statement=" + statement + ", parameter=" + parameter, e);
                }
            }
        }
    }
}